package app.action;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Vector;

import org.apache.log4j.Logger;

public abstract class AbsClient extends Thread {
	private final static Logger logger = Logger.getLogger(AbsClient.class);
	private Socket socket;
	private User me;

	private ObjectOutputStream oos;
	private ObjectInputStream ois;
	private Vector<Integer> targetIDs;

	protected final Vector<User> users; // Hold online user

	public AbsClient(String host, int port, String name) throws IOException, UnknownHostException {
		this.me = new User(-1, name);
		socket = new Socket(host, port);
		this.oos = new ObjectOutputStream(socket.getOutputStream());
		this.ois = new ObjectInputStream(socket.getInputStream());

		this.targetIDs = new Vector<>();
		this.users = new Vector<>();
	}

	@Override
	public void run() {
		Object obj = null;
		try {
			// Send out initial information
			MessageHandle ms = new MessageHandle(me, me.getName(), null);
			sendMessage(ms);

			// Process message
			while ((obj = ois.readObject()) != null) {
				MessageHandle msHnd = (MessageHandle) obj;
				switch (msHnd.getType()) {
					case SYSTEM:
						doProcessSystemMessage(msHnd);
						break;
					case GENERAL:
					case PRIVATE:
						doRenderingMessage(msHnd);
						break;
					default:
						break;
				}
			}
		}catch (IOException e) {
			logger.error(e.getMessage(), e.getCause());
		}catch (ClassNotFoundException e) {
			logger.error(e.getMessage(), e.getCause());
		}finally {
			try {
				ois.close();
				oos.close();
				socket.close();
			}catch (IOException e) {
				logger.error(e.getMessage(), e.getCause());
			}
		}
	}

	private void doProcessSystemMessage(MessageHandle msHnd) {
		// Update online users list
		users.clear();
		for(Object obj : msHnd.getContentObjs()) {
			if(obj instanceof User) {
				users.add((User) obj);
			}
		}

		// Set my own ID
		if(me.getID() < 0) {
			me.setID(Integer.valueOf(msHnd.getContent()));
		}
		// Process system message
		processSystemMessage();
	}

	/**
	 * This method is called when an user is going to be offline or online.<br>
	 * Utilizing to show who is online.
	 */
	protected abstract void processSystemMessage();

	protected abstract void doRenderingMessage(MessageHandle obj);

	protected void sendMessage(MessageHandle ms) {
		try {
			oos.writeObject(ms);
			oos.flush();
		}catch (IOException e) {
			logger.error(e.getMessage(), e.getCause());
		}
	}

	public int getMyID() {
		return me.getID();
	}

	public User getMe() {
		return me;
	}

	public Vector<Integer> getTargetIDs() {
		return targetIDs;
	}

	public void setTargetIDs(Vector<Integer> targetIDs) {
		this.targetIDs = targetIDs;
	}

	public void setReceiver(int userID) {
		this.targetIDs.add(userID);
	}
}
